Python:文本数据增强
👇 连享会 · 推文导航 | www.lianxh.cn
🍎 Stata:Stata基础 | Stata绘图 | Stata程序 | Stata新命令 📘 论文:数据处理 | 结果输出 | 论文写作 | 数据分享 💹 计量:回归分析 | 交乘项-调节 | IV-GMM | 时间序列 | 面板数据 | 空间计量 | Probit-Logit | 分位数回归 ⛳ 专题:SFA-DEA | 生存分析 | 爬虫 | 机器学习 | 文本分析 🔃 因果:DID | RDD | 因果推断 | 合成控制法 | PSM-Matching 🔨 工具:工具软件 | Markdown | Python-R-Stata 🎧 课程:公开课-直播 | 计量专题 | 关于连享会
连享会课程 · 2023 暑期班
⛳ Stata 系列推文:
全部 | Stata入门 | Stata教程 | Stata资源 | Stata命令 计量专题 | 论文写作 | 数据分享 | 专题课程 结果输出 | Stata绘图 | 数据处理 | Stata程序 回归分析 | 面板数据 | 交乘项-调节 | IV-GMM 内生性-因果推断 | 倍分法DID | 断点回归RDD | PSM-Matching | 合成控制法 Probit-Logit | 时间序列 | 空间计量 | 分位数回归 | 生存分析 | SFA-DEA 文本分析-爬虫 | Python-R-Matlab | 机器学习 Markdown | 工具软件 | 其它
☝ PDF下载 - 推文合集
作者:张洪洋 (西南财经大学)
邮箱:1750410339@qq.com
温馨提示: 文中链接在微信中无法生效。请点击底部「阅读原文」。或直接长按/扫描如下二维码,直达原文:
编者按:本文主要参考自 西南财经大学国际联合实验室《利用Python学习NLP》课件,特此致谢!
目录
1. 文本数据截断
2. 数据扩充
2.1 直接复制
2.2 反译
3. 噪声技术
3.1 同义词替换
3.2 随机插入
3.3 随机删除
4. 相关推文
文本数据增强可以简单理解为由少量数据生成大量数据的过程。一般比较成功的神经网络拥有大量参数,使这些参数正确工作需要用大量的数据进行训练,但实际情况是数据并没有那么多,因此需要做数据增强。
相较于图像、语音方面的数据增强而言,文本的数据增强更具有挑战性,因为文本中微小的改动就可能带来语义的变化,从而破坏了原文本,导致数据质量降低。本文将介绍通用几种文本数据增强方法,除了这些通用的增强方法之外,大多数时候需要针对具体的 NLP 任务设计对应的文本数据增强方法。
1. 文本数据截断
在处理长文本的过程中,通常需要按照某个阈值对文本进行截断,但是又不能直接在当前阈值截断,可能会破坏句子结构。比较理想的方案是,在某一个阈值向前搜索并找到句子的末位 (例如句号) 处进行截断,剩下的若还超过阈值,则重复截断。下面以 example_text_data.csv 这个文件为例。
import pandas as pd
data = pd.read_csv(r'https://file.lianxh.cn/data/e/example_text_data.csv')
data['text_len'] = data['text'].map(len)
data['text_len'].describe()
count 600.0000
mean 223.5000
std 281.2513
min 25.0000
25% 121.0000
50% 163.0000
75% 268.2500
max 5949.0000
Name: text_len, dtype: float64
可以看到大部分文本长度在 300 以内,存在少量超长文本。一般而言,为了避免维度过大以及提升计算效率,大多数 NLP 模型将文本的输入长度限制在了 512 或 1024 之内,这就意味着对于少量的超长文本,需要抛弃或者进行文本截断操作。
truncated_data = pd.DataFrame(columns=['id','text']) #新建dataframe
max_len = 300 #假设句子最长为300
for j in range(len(data)):
text = data['text'][j] #text
left_index = 0 #设定句子的左索引,默认为0
temp_text_list = [] #list
count = 0
df = {} #存放新增的内容
while len(text[left_index:])>= max_len :
count +=1
for z in range(100): # 只需向前搜索一定步数,加快运算
if text[left_index+max_len-z] in ['!','。','>',';','•',') '] :
#出现list里的这些符号认为为句子末尾,可自定义
temp_text_list.append(text[left_index: left_index+300-z+1])
#截断的第一个句子
left_index += max_len-z+1 # 将左索引右移到截断除
break
if count>=40: #可能会出现长文本里面没有结尾的情况,限制搜索范围为max_len*4个字节
break
temp_text_list.append(text[left_index:]) #截断的句子存放处
for i in temp_text_list:
df["id"] = data['id'][j] #装入df
df["text"] = i
truncated_data = truncated_data.append(df,ignore_index=True) # 向dataframe内添加行
truncated_data['text_len'] = truncated_data['text'].map(len)
truncated_data['text_len'].describe()
count 681.00000
mean 196.91630
std 193.08095
min 10.00000
25% 121.00000
50% 164.00000
75% 258.00000
max 4266.00000
Name: text_len, dtype: float64
2. 数据扩充
机器学习模型需要足够的数据支撑才能进行更好地训练,但实际生活中,作为开发者往往无法获取大量的数据,而专业的数据采集和标注公司提供的数据服务也并不便宜。因此,解决此问题有一个较为不错的初级方案,那就是进行数据扩充。
数据扩充 (data augmentation) 的本质:缺少海量数据时,为了保证模型的有效训练,一分钱掰成两半花。这里介绍两种数据扩充的方法。
2.1 直接复制
在 Python 中,直接用等于符号或者运用 copy()
函数可以返回一个复制。然而单纯地运用等于符号和 copy()
函数,只会返回一个浅复制 (浅拷贝)。
对于浅复制而言,其中一个发生变化,另一个被复制的对象或原对象也会发生变化。而对于深复制(深拷贝)而言,一旦复制出来了,就是独立的,其中一个发生变化,另一个不会随之发生变化。因此,增加数据量最简单和直接的方法就是直接复制拷贝一份新的数据。
new_data = data.copy(deep=True) #深复制
data = pd.concat([new_data,data], ignore_index = True) #pd.concat将两个dataframe进行拼合
print(f"扩充后的数据data.shape: {data.shape}")
扩充后的数据data.shape: (1200, 3)
2.2 反译
对于文本数据而言,将文本翻译成其他语言再翻译成回来后,利用翻译器的特点,使得翻译回来的文本与原来的文本语义高度相似,又引入了其他词汇,增加了模型可学习的内容。gooletrans 是一个免费的调用谷歌翻译库的 python api,可以通过以下代码安装:
!pip install googletrans==4.0.0rc1
下面利用 gooletrans 来进行中文的反译操作:
import csv
from googletrans import Translator
translator = Translator(service_urls=[
'translate.google.cn'
]) #接入谷歌的翻译服务,设定service_urls为translate.google.cn即可翻译中文
translator.raise_Exception = True #避免ip被限制
chinese_text = '下面的代码,我只涉及到了“中文”和“英语”两种语言的循环翻译,没有涉及到其他的语言。'
print(f"原句子: \n{chinese_text}\n")
english_text = translator.translate(chinese_text).text # 调用translate方法
print(f"汉译英: \n{english_text}\n")
re_chinese_text = translator.translate(english_text,dest='zh-CN').text # 设定为英译汉
print(f"英译汉:\n{re_chinese_text}\n")
原句子:
下面的代码,我只涉及到了“中文”和“英语”两种语言的循环翻译,没有涉及到其他的语言。
汉译英:
In the following code, I only involve cyclic translations of two languages:
"Chinese" and "English", which does not involve other languages.
英译汉:
在以下代码中,我仅涉及两种语言的循环翻译:“中文”和“英语”,不涉及其他语言。
将 example_data 进行批量的反译:
import pandas as pd
data = pd.read_csv('example_text_data.csv') # 读入
trans_data = data.copy(deep = True) #深拷贝
translator.raise_Exception = True #避免ip被限制
trans_data['trans_text'] = trans_data['text'][:5].map(lambda x: \
translator.translate(translator.translate(x).text,dest='zh-CN').text)
# map和lambda函数是dataframe中非常重要的函数,可以很方便的灵活的对dataframe进行批量操作
display(trans_data.head())
3. 噪声技术
噪声技术旨在通过向文本中注入噪声数据,来生成新的文本,最后使得训练的模型对扰动具有鲁棒性。EDA (Easy Data Augmentation) 是一种通用且易于实现的 nlp 噪声技术,能显著提高卷积和递归神经网络的性能,仅使用 50% 的可用训练集进行 EDA 训练便达到了与使用所有可用数据进行正常训练相同的准确度。
EDA由四个简单但功能强大的操作组成:同义词替换、随机插入、随机交换和随机删除。
3.1 同义词替换
同义词替换(Synonym Replacement, SR)是指从句子中随机选取 n 个不属于非停止词,并随机选择其同义词替换它们。
!pip install synonyms
#若未安装synonoyms,运行此代码,若synonyms安装错误,请检查是否有安装jieba等前置库
import jieba
import random
from random import shuffle
import sys
import synonyms
#一个中文近义词工具包,开源地址在https://github.com/chatopera/Synonyms
#synonyms第一次调包可能会很慢,您可以选择将文件夹中的"words.vector.gz"文件放入该包路径".//synonyms//data//"中,
#以anaconda为例,路径为应该为"//anaconda//lib//site-packages//synonyms//data//"
我们通过 synonyms.display()
和 synonyms.nearby()
来具体看一下 synonyms
是怎么展示中文同义词结果。
synonyms.display("飞机")
#synonyms.display()将输出"飞机"的近义词组,并以单词相似性由高到低排序
'飞机'近义词:
1. 飞机:1.0
2. 直升机:0.84233904
3. 客机:0.83930016
4. 滑翔机:0.7872388
5. 军用飞机:0.7832081
6. 水上飞机:0.77857226
7. 运输机:0.77247417
8. 航机:0.7664748
9. 航空器:0.76592904
10. 民航机:0.74209654
synonyms.nearby("飞机")
#synonyms.nearby()返回一个包含"近义词"和"该近义词得分"的元组,二者均以列表的形式存储
(['飞机', '直升机', '客机', '滑翔机', '军用飞机', '水上飞机', '运输机', '航机', '航空器', '民航机'],
[1.0,
0.84233904,
0.83930016,
0.7872388,
0.7832081,
0.77857226,
0.77247417,
0.7664748,
0.76592904,
0.74209654])
接下来我们将利用同义词库去构建 EDA 中的相关方法,包括同义词替换和随机插入。
random.seed(2022) #设置随机种子
#构建停用词列表
with open('停用词.txt', 'r',encoding='utf-8') as f:
stop_words = [s.rstrip() for s in f.readlines()]
#分词
sentence = "我也期待着能和你飞去同一片土地"
words = jieba.lcut(sentence) # 调用jieba进行分词
print(words) # 分词结果
['我', '也', '期待', '着', '能', '和', '你', '飞去', '同', '一片', '土地']
进行同义词替换主要只进行二步,第一步是找到同义词列表,第二部是随机选择同义词列表中的一个词进行替换。
# 同义词替换:替换一个语句中的n个单词为其同义词
def synonym_replacement(words, n):
new_words = words.copy() #深拷贝
random_word_list = list(set([word for word in words if word not in stop_words]))
#生成非停止词列表
random.shuffle(random_word_list)
#将列表中元素打乱顺序,便于后续随机选择同义词进行替换
num_replaced = 0
#被替换的词的数量,初始为0
for random_word in random_word_list:
synonyms_word = synonyms.nearby(random_word)[0]
#调用刚才定义的函数get_synonyms,返回意思最相近的词语
if len(synonyms_word) >= 1:
synonym = random.choice(synonyms_word)
#在同义词列表中随机选择一个同义词
new_words = [synonym if word == random_word else word for word in new_words]
#将随机选择的同义词添加进词表,并替换原单词
num_replaced += 1
if num_replaced >= n: #当n个单词被替换后结束循环
break
return new_words
print(f"替换前:\n{words}\n")
print(f"替换后:\n{synonym_replacement(words, 1)}\n")
#调用synonym_replacement,设置n=1,即随机替换一个同义词
替换前:
['我', '也', '期待', '着', '能', '和', '你', '飞去', '同', '一片', '土地']
替换后:
['我', '也', '期待', '着', '能', '和', '你', '飞去', '同', '一片', '田地']
3.2 随机插入
随机插入 (Random Insertion,RI) 是指从句子中随机选择一个非停止词,然后找到它对应的同义词,并将其插入到句子中的一个随机位置
# 随机插入n个同义词
def add_word(new_words):
synonyms = [] # 同义词表
counter = 0 # 计数器
random_word_list = list(set([word for word in new_words if word not in stop_words]))
#生成非停止词列表
while len(synonyms) < 1:
#这里主要指随机选择random_word_list中一个词
random_word = random_word_list[random.randint(0, len(random_word_list)-1)]
#random.randint()方法随机返回指定范围内的一个整数,
#并且可以取到右侧边界值,为防止索引超出列表容量,因此会减1
synonyms = get_synonyms(random_word)
#调用上面定义的函数get_synonyms,返回一个包含10个同义词的list
counter += 1
if counter >= 10:
return
random_synonym = random.choice(synonyms) #从同义词中随机进行选择
random_idx = random.randint(0, len(new_words)-1) #选择一个随机索引,便于随机选择的同义词插入
new_words.insert(random_idx, random_synonym) #在随机索引处插入同义词
def random_insertion(words, n): #调用随机插入方法,重复n次
new_words = words.copy()
for _ in range(n):
add_word(new_words) # 调用上面定义的函数add_word
return new_words
print(f"插入前:\n{words}\n")
print(f"插入后:\n{random_insertion(words, 1)}\n") #随机插入n =1个同义词
插入前:
['我', '也', '期待', '着', '能', '和', '你', '飞去', '同', '一片', '土地']
插入后:
['我', '也', '期待', '着', '能', '和', '你', '飞去', '同', '盼望', '一片', '土地']
3.3 随机删除
随机删除 (Random Deletion,RD) 指以概率 随机删除句子中的单词。
def random_deletion(words, p):
if len(words) == 1: #至少要大于1个单词才能随机删除
return words
new_words = []
for word in words:
r = random.uniform(0, 1) #随机生成一个在 (0,1) 范围内的实数
if r > p: #以1-p的概率向新词列表添加单词
new_words.append(word)
if len(new_words) == 0: #若删除概率p为1,则随机保留词表中的一个单词
rand_int = random.randint(0, len(words)-1)
return [words[rand_int]]
return new_words
print(f"删除前:\n{words}\n")
print(f"删除后:\n{random_deletion(words, 0.1)}\n") #以0.1的概率随机删除单词
删除前:
['我', '也', '期待', '着', '能', '和', '你', '飞去', '同', '一片', '土地']
删除后:
['我', '也', '期待', '着', '能', '和', '你', '飞去', '同', '一片', '土地']
4. 相关推文
Note:产生如下推文列表的 Stata 命令为:
lianxh 文本, m
安装最新版lianxh
命令:
ssc install lianxh, replace
专题:专题课程 ⏩ 专题课:文本分析-爬虫-机器学习-2022年4月 连享会:助教入选通知-2022文本分析与爬虫 ⚽助教招聘:文本分析-爬虫-机器学习 助教入选结果 - 连享会 文本分析与爬虫直播课 专题:数据分享 数据库分享:年报文本语气数据库 专题:Stata命令 Stata:计算文本语调-onetext 专题:文本分析-爬虫 Stata文本分析:lsemantica-潜在语义分析的文本相似性判别 textfind:文本分析之词频分析-TF-IDF Stata文本分析之-tex2col-命令-文字变表格 Stata: 正则表达式和文本分析 专题:Python-R-Matlab Python文本分析:将词转换为向量-Word2Vec Python:文本分析必备—搜狗词库 Python: 使用正则表达式从文本中定位并提取想要的内容 专题:公开课 ⭐ 公开课:王菲菲-文本分析在经济金融领域的应用
课程推荐:2023 暑期班
主讲老师:连玉君,王群勇
🍓 课程主页:https://www.lianxh.cn/news/fdc69c3695aec.html
New! Stata 搜索神器:
lianxh
和songbl
GIF 动图介绍
搜: 推文、数据分享、期刊论文、重现代码 ……
👉 安装:
. ssc install lianxh
. ssc install songbl
👉 使用:
. lianxh DID 倍分法
. songbl all
🍏 关于我们
连享会 ( www.lianxh.cn,推文列表) 由中山大学连玉君老师团队创办,定期分享实证分析经验。 直通车: 👉【百度一下: 连享会】即可直达连享会主页。亦可进一步添加 「知乎」,「b 站」,「面板数据」,「公开课」 等关键词细化搜索。